unit hadecriteria;

{$mode objfpc}{$H+}

interface

uses
  Classes,
  SysUtils,
  hadebaseobject;

type

  THadeOrderType = (oAsc, oDesc);

  IHadeExpression = interface
    ['{0080AB39-80D2-4F96-B546-15725FFB484E}']
    function getClause: string;
  end;

  { THadeBaseExpression }

  THadeBaseExpression = class(THadeBaseObject, IHadeExpression)
  private
    FColName: string;
    FValue: variant;
  protected
    function getStringLiteral(const AValue: variant): string;
    function symbol: string; virtual; abstract;
  public
    constructor Create(const AColName: string; const AValue: variant); virtual;

    function getClause: string; virtual;
  end;

  { THadeEqual }

  THadeEqual = class(THadeBaseExpression)
  protected
    function symbol: string; override;
  end;

  { THadeNotEqual }

  THadeNotEqual = class(THadeBaseExpression)
  protected
    function symbol: string; override;
  end;

  { THadeLessThan }

  THadeLessThan = class(THadeBaseExpression)
  protected
    function symbol: string; override;
  end;

  { THadeGreaterThan }

  THadeGreaterThan = class(THadeBaseExpression)
  protected
    function symbol: string; override;
  end;


  { THadeLessAndEqualThan }

  THadeLessAndEqualThan = class(THadeBaseExpression)
  protected
    function symbol: string; override;
  end;

  { THadeGreaterAndEqualThan }

  THadeGreaterAndEqualThan = class(THadeBaseExpression)
  protected
    function symbol: string; override;
  end;

  { THadeBaseIn }

  THadeBaseIn = class(THadeBaseExpression)
  private
    FValues: array of variant;
  public
    constructor Create(const AColName: string; const AValue: array of variant);
      reintroduce;

    function getClause: string; override;
  end;

  { THadeIn }

  THadeIn = class(THadeBaseIn)
  public
    function symbol: string; override;
  end;

  { THadeNotIn }

  THadeNotIn = class(THadeBaseIn)
  public
    function symbol: string; override;
  end;

  { THadeLike }

  THadeLike = class(THadeBaseExpression)
  public
    function symbol: string; override;
  end;

  { THadeNotLike }

  THadeNotLike = class(THadeBaseExpression)
  public
    function symbol: string; override;
  end;

  { THadeBaseBetween }

  THadeBaseBetween = class(THadeBaseExpression)
  private
    fFirstValue, fSecondValue: variant;
  public
    constructor Create(const AColName: string; const firstValue: variant;
      const secondValue: variant);
      reintroduce;

    function getClause: string; override;
  end;

  { THadeBetween }

  THadeBetween = class(THadeBaseBetween)
  public
    function symbol: string; override;
  end;

  { THadeNotBetween }

  THadeNotBetween = class(THadeBaseBetween)
  public
    function symbol: string; override;
  end;

  { IHadeCriteria }

  IHadeCriteria = interface
    ['{C92EE2E0-6506-446D-942A-4D031E0F21F5}']
    procedure Equal(const AColName: string; const AValue: variant);
    procedure notEqual(const AColName: string; const AValue: variant);
    procedure Add_Or();

    procedure LessThan(const AColName: string; const AValue: variant);
    procedure LessAndEqualThan(const AColName: string; const AValue: variant);

    procedure GreaterThan(const AColName: string; const AValue: variant);
    procedure GreaterAndEqualThan(const AColName: string; const AValue: variant);

    procedure Like(const AColName: string; const AValue: variant);
    procedure notLike(const AColName: string; const AValue: variant);

    procedure _in(const AColName: string; AValues: array of variant);
    procedure notIn(const AColName: string; AValues: array of variant);

    function getClause(): string;
  end;

  { THadeBufferString }
  { very simple 'helper' class }
  THadeBufferString = class
  private
    s: string;
  public
    procedure Append(const AString: string);
  end;

  { THadeCriteria }

  THadeCriteria = class(THadeBaseObject, IHadeCriteria)
  private
    FExpression: IHadeExpression;
    fBufferString: THadeBufferString;
    fFreeAfterUse: boolean;
    FMappingManager: TObject;
  protected
    procedure AppendToBuffer();
  public
    procedure Equal(const AColName: string; const AValue: variant);
    procedure notEqual(const AColName: string; const AValue: variant);

    procedure Add_Or();

    procedure LessThan(const AColName: string; const AValue: variant);
    procedure LessAndEqualThan(const AColName: string; const AValue: variant);

    procedure GreaterThan(const AColName: string; const AValue: variant);
    procedure GreaterAndEqualThan(const AColName: string; const AValue: variant);

    procedure Like(const AColName: string; const AValue: variant);
    procedure notLike(const AColName: string; const AValue: variant);

    procedure _in(const AColName: string; AValues: array of variant);
    procedure notIn(const AColName: string; AValues: array of variant);

    procedure Between(const AColName: string; const FirstValue: variant;
      const secondValue: variant);

    procedure notBetween(const AColName: string; const FirstValue: variant;
      const secondValue: variant);

    function getClause(): string;

    function AutoFreeAfterUse: boolean;

    {* if set AAutoFreeAfterUse To False you responsible for manual freeing criteria.
       you set to False when you need to reuse criteria
       for other read operation *}
    constructor Create(const AObj: TObject; const AConnectionName: string = '';
      const AAutoFreeAfterUse: boolean = True);
    destructor Destroy; override;
  end;

implementation

uses
  variants,
  hadeutils,
  hademapping,
  hadeobject;

{ THadeNotBetween }

function THadeNotBetween.symbol: string;
begin
  Result := ' NOT BETWEEN ';
end;

{ THadeBetween }

function THadeBetween.symbol: string;
begin
  Result := ' BETWEEN ';
end;

{ THadeBaseBetween }

constructor THadeBaseBetween.Create(const AColName: string;
  const firstValue: variant; const secondValue: variant);
begin
  self.FColName := AColName;
  fFirstValue := FirstValue;
  fSecondValue := SecondValue;
end;

function THadeBaseBetween.getClause: string;
begin
  Result := fColName + symbol + getStringLiteral(fFirstValue) +
    ' AND ' + getStringLiteral(fSecondValue);
end;

{ THadeNotLike }

function THadeNotLike.symbol: string;
begin
  Result := ' NOT LIKE ';
end;

{ THadeLike }

function THadeLike.symbol: string;
begin
  Result := ' LIKE ';
end;

{ THadeBufferString }

procedure THadeBufferString.Append(const AString: string);
begin
  s := s + AString;
end;

{ THadeCriteria }

procedure THadeCriteria.AppendToBuffer;
begin
  FBufferString.Append(FExpression.getClause);
  FBufferString.Append(' AND ');
end;

procedure THadeCriteria.Equal(const AColName: string; const AValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeEqual.Create(lColName, AValue);
  AppendToBuffer();
end;

procedure THadeCriteria.notEqual(const AColName: string; const AValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeNotEqual.Create(lColName, AValue);
  AppendToBuffer();
end;

procedure THadeCriteria.Add_Or;
begin
  if FBufferString.s = '' then
    Exit;
  System.Delete(FBufferString.s, Length(FBufferString.s) - 4, 5);
  FBufferString.Append(' OR ');
end;

procedure THadeCriteria.LessThan(const AColName: string; const AValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeLessThan.Create(lColName, AValue);
  AppendToBuffer();
end;

procedure THadeCriteria.LessAndEqualThan(const AColName: string; const AValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeLessAndEqualThan.Create(lColName, AValue);
  AppendToBuffer();
end;

procedure THadeCriteria.GreaterThan(const AColName: string; const AValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeGreaterThan.Create(lColName, AValue);
  AppendToBuffer();
end;

procedure THadeCriteria.GreaterAndEqualThan(const AColName: string;
  const AValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeGreaterAndEqualThan.Create(lColName, AValue);
  AppendToBuffer();
end;

procedure THadeCriteria.Like(const AColName: string; const AValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeLike.Create(lColName, AValue);
  AppendToBuffer();
end;

procedure THadeCriteria.notLike(const AColName: string; const AValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeNotLike.Create(lColName, AValue);
  AppendToBuffer();
end;

procedure THadeCriteria._in(const AColName: string; AValues: array of variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeIn.Create(lColName, AValues);
  AppendToBuffer();
end;

procedure THadeCriteria.notIn(const AColName: string; AValues: array of variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadenotIn.Create(lColName, AValues);
  AppendToBuffer();
end;

procedure THadeCriteria.Between(const AColName: string; const FirstValue: variant;
  const secondValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeBetween.Create(lColName, firstValue, secondValue);
  AppendToBuffer();
end;

procedure THadeCriteria.notBetween(const AColName: string;
  const FirstValue: variant; const secondValue: variant);
var
  lColName: string;
begin
  lColName := THadeMapClass(fMappingManager).ColNameByProp(AColName);
  FExpression := THadeNotBetween.Create(lColName, firstValue, secondValue);
  AppendToBuffer();
end;

function THadeCriteria.getClause: string;
var
  tmp: string;
begin
  tmp := Trim(FBufferString.s);// <== accessing Private data of TBufferString
  System.Delete(tmp, Length(tmp) - 3, 4);
  Result := tmp;
end;

function THadeCriteria.AutoFreeAfterUse: boolean;
begin
  Result := fFreeAfterUse;
end;

constructor THadeCriteria.Create(const AObj: TObject; const AConnectionName: string;
  const AAutoFreeAfterUse: boolean);
var
  eleName: string;
begin
  fFreeAfterUse := AAutoFreeAfterUse;
  FBufferString := THadeBufferString.Create;

  eleName := AObj.ClassName;
  if AObj is THadeObjectList then
    eleName := GHadeMappingManager.Find(AConnectionName).RegObjecByName(
      AObj.ClassName).ClassBOName;
  FMappingManager := GHadeMappingManager.Find(AConnectionName).ClassByName(eleName);
end;

destructor THadeCriteria.Destroy;
begin
  FreeAndNil(FBufferString);
  inherited Destroy;
end;

{ THadeBaseExpression }

function THadeBaseExpression.getStringLiteral(const AValue: variant): string;
begin
  { don't worry VarIsStr just simple Case of , not impact performance }
  if VarIsStr(AValue) then
    Result := QuotedStr(AValue)
  else
    Result := varToStr(AValue);
end;

constructor THadeBaseExpression.Create(const AColName: string; const AValue: variant);
begin
  FColName := AColName;
  FValue := AValue;
end;

function THadeBaseExpression.getClause: string;
begin
  Result := FColName + symbol + getStringLiteral(FValue);
end;

{ THadeGreaterAndEqualThan }

function THadeGreaterAndEqualThan.symbol: string;
begin
  Result := ' >= ';
end;

{ THadeLessAndEqualThan }

function THadeLessAndEqualThan.symbol: string;
begin
  Result := ' <= ';
end;

{ THadeNotIn }

function THadeNotIn.symbol: string;
begin
  Result := ' NOT IN ';
end;

{ THadeBasein }

constructor THadeBaseIn.Create(const AColName: string; const AValue: array of variant);
var
  iloop: integer;
begin
  FColName := AColName;

  setLength(FValues, HIGH(AValue) + 1);
  for iloop := 0 to HIGH(AValue) do
  begin
    FValues[iloop] := AValue[iloop];
  end;
end;

function THadeBaseIn.getClause: string;
var
  iloop: integer;
  tmp: string;
begin
  for iloop := 0 to HIGH(FValues) do
  begin
    tmp := tmp + getStringLiteral(FValues[iloop]) + ',';
  end;
  deleteLastChar(tmp);
  Result := FColName + symbol + '(' + tmp + ')';
end;

{ THadeIn }

function THadeIn.symbol: string;
begin
  Result := ' IN ';
end;

{ THadeGreaterThan }

function THadeGreaterThan.symbol: string;
begin
  Result := ' > ';
end;

{ THadeLessThan }

function THadeLessThan.symbol: string;
begin
  Result := ' < ';
end;

{ THadeNotEqual }

function THadeNotEqual.symbol: string;
begin
  Result := ' <> ';
end;


{ THadeEqual }

function THadeEqual.symbol: string;
begin
  Result := ' = ';
end;

end.

